package com.qianli.cashmama.trade.application.bill;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.fqgj.common.api.Response;
import com.fqgj.common.api.annotations.ParamsValidate;
import com.fqgj.common.utils.*;
import com.fqgj.exception.common.ApplicationException;
import com.fqgj.xjd.user.client.enums.FrozenCodeEnum;
import com.fqgj.xjd.user.client.enums.UserEventTrackEnum;
import com.fqgj.xjd.user.client.response.User;
import com.google.common.base.Strings;
import com.qianli.cashmama.product.facade.result.ProductCategory;
import com.qianli.cashmama.trade.client.bill.BillApplicationService;
import com.qianli.cashmama.trade.client.bill.request.BillPayRequest;
import com.qianli.cashmama.trade.client.bill.request.DeductOverdueFeeRequest;
import com.qianli.cashmama.trade.client.bill.request.SelfLoanConfirmationRequest;
import com.qianli.cashmama.trade.client.bill.response.ApproveConclusionResponse;
import com.qianli.cashmama.trade.client.bill.response.BillRepaymentResponse;
import com.qianli.cashmama.trade.client.bill.response.OrderStatusResponse;
import com.qianli.cashmama.trade.client.bill.response.RepaymentPlanDetailResponse;
import com.qianli.cashmama.trade.client.enums.OrderStatusSwitchEnum;
import com.qianli.cashmama.trade.client.vo.DiscountListVO;
import com.qianli.cashmama.trade.common.config.TimerRecord;
import com.qianli.cashmama.trade.common.encryption.ReceiveData;
import com.qianli.cashmama.trade.common.encryption.StandardUtil;
import com.qianli.cashmama.trade.common.enums.*;
import com.qianli.cashmama.trade.common.error.TradeErrorCodeEnum;
import com.qianli.cashmama.trade.common.exceptions.TradeException;
import com.qianli.cashmama.trade.common.mq.MQTradeActionEnum;
import com.qianli.cashmama.trade.common.utils.CalculatorUtils;
import com.qianli.cashmama.trade.common.utils.HttpUtil;
import com.qianli.cashmama.trade.common.utils.TradeConfigUtil;
import com.qianli.cashmama.trade.dao.*;
import com.qianli.cashmama.trade.domain.factory.BillBaseDomainFactory;
import com.qianli.cashmama.trade.domain.model.*;
import com.qianli.cashmama.trade.domain.services.TradeBaseService;
import com.qianli.cashmama.trade.domain.services.TradeDiscountService;
import com.qianli.cashmama.trade.entity.TBillEntity;
import com.qianli.cashmama.trade.entity.TTradeEntity;
import com.qianli.cashmama.trade.entity.TTradeExtraEntity;
import com.qianli.cashmama.trade.integration.notification.message.MessageIntegrationService;
import com.qianli.cashmama.trade.integration.notification.message.dto.RepaymentMsgDto;
import com.qianli.cashmama.trade.integration.notification.message.utils.MessageUtils;
import com.qianli.cashmama.trade.integration.notification.message.utils.PushUtils;
import com.qianli.cashmama.trade.integration.product.ProductIntegrationService;
import com.qianli.cashmama.trade.integration.user.UserIntegrationService;
import com.qianli.cashmama.trade.mq.MQOpenapiHelpUtils;
import com.qianli.cashmama.trade.repo.*;
import com.qianli.cashmama.trade.unique.TradeUniqueService;
import com.qianli.user.enums.UserAuthEnum;
import com.qianli.user.enums.UserAuthStatusEnum;
import com.robert.vesta.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: sm
 * Date: 2017/7/20
 * Time: 上午9:32
 */
@Service("billApplicationService")
public class BillApplicationServiceImpl implements BillApplicationService {
    private static final Logger LOGGER = LoggerFactory.getLogger(BillApplicationServiceImpl.class);
    @NacosValue(value = "${inner.push.token:8wIGjP1vTkAGhht1}", autoRefreshed = true)
    private String token;
    @NacosValue(value = "${inner.sync.push.domain:http://47.98.115.75:8087/gateway/6/38/gupta}", autoRefreshed = true)
    private String pushUrlDomain;
    @NacosValue(value = "${app.id:91C1000}", autoRefreshed = true)
    private String APP_ID;

    private static final String SELF_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDevikTyL2tHHPZOPA279x6EKDd7+vIgtxODRzfti+pgGJKzX7oQNVwlBre7leGckRMsyB1pk3wZ/JrXNVzy8kv84RrAJsZh+Y/E2EwFOSH4INcEbwHxSjBC2ELXB2dXC01l1GGHqREk1b4oBALoT7bugji89uBY/NO0G2dfHm1HQIDAQAB";

    private static final String CO_PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMsFDM3N4rAZAtMNxXqbPhiqrSm2TMvveTJkJRX4vY2gAKix2jiHmZQQjLRnm84TY/m6D6YePeOqjUyjExOcPL9Xs5u1DRSbRgUhrXVAm+E128UeIIGrgHrrDQYazsuW2FbFoPs04BGPU0+yaTUZO3u7d0hobfXL+AMuMMQM+pMLAgMBAAECgYBMpT1uPTpFzUt5rotNfGp3GYiCZOadX21cDt7Jarz1SOC2z15ZVSgv9AeK3ErABCYMU6gwyN4Io4zwz+gV0ThFQE3INHjhaqNyn5UfslM/vP003GvdfgbuKitGcIyuO51Zx6Qipqqn/3sHEiO7HvVJEtbrbYHyUG2g8PM58f8S4QJBAPCc4ClN9RG5hjyS6pANRKfiKtmB8Ts+sQZD9o1f+Svyi8faYk0UFeUKxl+h+W/qYuMu/AFoBqtE8KORvjw8MCkCQQDYALrm2V0gVXVdgFyMennepkAheWZEIkqDct2kNHVAis9DvddEfYwwA+PjEO4ywlpBglyfK9i2dZkwQCzEywATAkBNT/rvCq+OLHCnkgVwl/LRhuiBzzD1+mTjQH8+WfI0XtDvPIqNfcCzQc0P+Xmurj9sBDLRON27toEj7baUoDpRAkA/oj9tgL2hp+tyhz08mVxLznh4JBaPnfaLsQxAsr+UKxCSaVwArmfdQVgibbtusrj6MZhtv/IRFqXxhAZsV02HAkBkvTCFTfSBjt5amXjlK306AsqsxLjdSjcjpWXguzBirPzUkJCOicFhtnKdHbk34Ys8EFE9OF+ptGQVm15cHfB/";

    @Autowired
    private BillBaseRepository billBaseRepository;

    @Autowired
    private TradeBaseRepository tradeBaseRepository;

    @Autowired
    private BillBaseDomainFactory billBaseDomainFactory;

    @Autowired
    private TradeBillDetailRepository tradeBillDetailRepository;


    @Autowired
    private TradeDiscountService tradeDiscountService;

    @Autowired
    private TradeTaskCenterRepository tradeTaskCenterRepository;

    @Autowired
    private TradeUniqueService tradeUniqueService;

    @Autowired
    private MessageIntegrationService messageIntegrationService;

    @Autowired
    UserIntegrationService userIntegrationService;
    @Autowired
    private BillRepaymentAccountingRepository billRepaymentAccountingRepository;

    @Autowired
    private BillAccountingRepository billAccountingRepository;

    @Autowired
    private PushUtils pushUtils;

    @Autowired
    private TradeConfigUtil tradeConfigUtil;

    @Autowired
    TTaskRecordDao tTaskRecordDao;
    @Autowired
    MessageUtils messageUtils;

    @Autowired
    private TimeoutRepository timeoutRepository;


    @Autowired
    private DeductFlowRepository deductFlowRepository;

    @Autowired
    private TTimeoutCenterDao tTimeoutCenterDao;

    @Autowired
    private TTradeExtraDao tTradeExtraDao;

    @Autowired
    private TTradeDao tTradeDao;
    @Autowired
    private TBillDao tBillDao;
    @Autowired
    private ProductIntegrationService productIntegrationService;

    @Autowired
    private TradeBaseService tradeBaseService;


    @Override
    @ParamsValidate
    @TimerRecord
    public Response repaymentCallBack(BillPayRequest billPayRequest) {
        //目前这个接口只有admin后台手动还款的时候调用,之前单独处理逻辑,现在指到repaymentCallBackV2；
        LOGGER.info("repaymentCallBack -forward to repaymentCallBack,billPayRequest[{}]", JSON.toJSON(billPayRequest));
        Response response = this.repaymentCallBackV2(billPayRequest);
        LOGGER.info("repaymentCallBack -forward to repaymentCallBack, response[{}]", JSON.toJSON(response));
        return response;
    }

    @Override
    @ParamsValidate
    @TimerRecord
    public Response<BillRepaymentResponse> repaymentCallBackV2(BillPayRequest billPayRequest) {
        LOGGER.info("repaymentCallBackV2 param billPayRequest:{}", JSON.toJSON(billPayRequest));
        RepaymentMsgDto repaymentMsgDto = new RepaymentMsgDto();
        repaymentMsgDto.setRepaymentCapital(billPayRequest.getCapital());
        LOGGER.info("send repayment message repaymentMsgDto:{}", JSON.toJSON(repaymentMsgDto));
        try {
            List<String> tradeNoList = new ArrayList<>();
            Map<String, BillPaidStatusEnum> map = new HashMap<>();
            TradeBillDetail tradeBillDetail = null;
            try {
                BigDecimal discountCapital = BigDecimal.ZERO;
                List<DiscountListVO> discountList = billPayRequest.getDiscountList();
                if (CollectionUtils.isNotEmpty(discountList)) {//计算抵扣金额
                    String discountCapital1 = discountList.get(0).getDiscountCapital();
                    if (StringUtils.isNotEmpty(discountCapital1)) {
                        discountCapital = discountCapital.add(new BigDecimal(discountCapital1));
                    }
                }
                billPayRequest.setDiscountCapital(discountCapital);

                //记录还款流水
                BillBase billBaseTmp = null;
                if (CollectionUtils.isNotEmpty(billPayRequest.getBillNoList()) && billPayRequest.getBillNoList().size() == 1) {
                    billBaseTmp = billBaseRepository.getBillByBillNo(billPayRequest.getBillNoList().get(0));
                }
                tradeBillDetail = billBaseDomainFactory.composeBillDetail(billBaseTmp, billPayRequest, billPayRequest.getUserCode());
                tradeBillDetailRepository.store(tradeBillDetail);
            } catch (DuplicateKeyException e) {
                LOGGER.info("====repaymentCallBackV2 ====");
                return Response.ok().putData(new BillRepaymentResponse(map));
            }
            for (String billNo : billPayRequest.getBillNoList()) {
                BillBase billBase = billBaseRepository.getBillByBillNo(billNo);
                BillPaidStatusEnum paidStatusEnum = repayment(billBase, billPayRequest, tradeBillDetail);
                map.put(billNo, paidStatusEnum);

                if (!tradeNoList.contains(billBase.getTradeNo())) {
                    tradeNoList.add(billBase.getTradeNo());
                }
                billPayRequest.setTradeNo(billBase.getTradeNo());
            }

            if (CollectionUtils.isNotEmpty(tradeNoList)) {
                updateTrade(repaymentMsgDto, tradeNoList, billPayRequest.getPayDate());
                // 添加还款推送给KB任务
                //addLsmRepaymentTask(billPayRequest.getTripleNo(), tradeNoList.get(0));
            }
            // 发送还款完成短信
            sendRepaymentSucessMsg(billPayRequest, repaymentMsgDto);
            // notice
            try {
                TTradeExtraEntity tTradeExtraEntity = tTradeExtraDao.selectByTradeNo(billPayRequest.getTradeNo());
                if (tTradeExtraEntity != null && StringUtils.isNotEmpty(tTradeExtraEntity.getAppsflyerId())) {
                    messageIntegrationService.sendAppsflyerDataPush(tTradeExtraEntity.getPackageName(), tTradeExtraEntity.getAppsflyerId(), tTradeExtraEntity.getUserCode(), AppsFlyerEventEnum.REPAYMENT_SUCCESS.getEventName(), repaymentMsgDto.getRepaymentCapital());
                    LOGGER.info("==== push appsflyer data success tTradeExtraEntity:{}", JSONObject.toJSONString(tTradeExtraEntity));
                    TTradeEntity tTradeEntity = tTradeDao.selectOneByTradeNo(tTradeExtraEntity.getTradeNo());
                    if (tTradeEntity != null && tTradeEntity.getIsNewUser() == 1) {
                        messageIntegrationService.sendAppsflyerDataPush(tTradeExtraEntity.getPackageName(), tTradeExtraEntity.getAppsflyerId(), tTradeExtraEntity.getUserCode(), AppsFlyerEventEnum.NEW_SER_ONTIME_REPAYMENT.getEventName(), repaymentMsgDto.getRepaymentCapital());
                        LOGGER.info("=== push appsflyer new user repayment success tradeNo:{} ===", tTradeEntity.getTradeNo());
                    }
                }
                pushMsg(billPayRequest, tradeNoList.get(0), repaymentMsgDto);
            } catch (Exception e) {
                LOGGER.error("------ push exception ------", e);
            }

            return Response.ok().putData(new BillRepaymentResponse(map));
        } catch (TradeException e) {
            LOGGER.error("== 还款失败 ==,e", e);
            return Response.error(e.getErrorEnum());
        }
    }

    /**
     * 账单抵扣利息和罚息
     *
     * @param deductOverdueFeeRequest
     * @return
     */
    @ParamsValidate
    @Override
    @TimerRecord
    public Response deductOverdueFee(DeductOverdueFeeRequest deductOverdueFeeRequest) {
        LOGGER.info("deductOverdueFee开始,deductOverdueFeeRequest:{}", JSON.toJSONString(deductOverdueFeeRequest));
        Date expirationDate = null;
        if (StringUtils.isNotEmpty(deductOverdueFeeRequest.getExpirationDate())) {
            if (deductOverdueFeeRequest.getExpirationDate().length() == "yyyy-MM-dd".length()) {
                expirationDate = DateUtil.getDate(deductOverdueFeeRequest.getExpirationDate(), "yyyy-MM-dd");
            }
            if (deductOverdueFeeRequest.getExpirationDate().length() == "yyyy-MM-dd HH:mm:ss".length()) {
                expirationDate = DateUtil.getDate(deductOverdueFeeRequest.getExpirationDate(), "yyyy-MM-dd HH:mm:ss");
            }
        }
        if (expirationDate != null && expirationDate.getTime() < new Date().getTime()) {
            return Response.error("Expiration date is incorrect");
        }
        try {

            BillBase billBase = billBaseRepository.getBillByBillNo(deductOverdueFeeRequest.getBillNo());
            if (Objects.equals(billBase.getPaidStatus(), BillPaidStatusEnum.CLEAR_REPAYMENT)) {
                LOGGER.info("订单已结清,不再处理,billNo:{}", billBase.getBillNo());
                return Response.ok();
            }

            TradeBase tradeBase = tradeBaseRepository.load(billBase.getTradeNo());

            Date date = new Date();
            boolean isOverdue = CalculatorUtils.isOverdue(billBase.getRepaymentDate(), date);
            BigDecimal overdueServiceFee = BigDecimal.ZERO;
            BigDecimal overdueFee = BigDecimal.ZERO;
            if (isOverdue) {
                int overdueDays = CalculatorUtils.overDueDays(billBase.getRepaymentDate(), date);
                overdueServiceFee = tradeBase.getOverdueServiceFee();
                overdueFee = CalculatorUtils.calculateOverdueFee(billBase.getBorrowCapital(), tradeBase.getOverdueRatio(), overdueDays);
            }
            //计算剩余应还
            BigDecimal repaymentAmount = CalculatorUtils.calculateRepaymentCapital(billBase.getRepaymentCapital(), overdueFee, overdueServiceFee, billBase.getPaidCapital());
            //当前实际还款金额
            BigDecimal realPaidCapital = billBase.getDiscountCapital() == null ? billBase.getPaidCapital() : billBase.getPaidCapital().subtract(billBase.getDiscountCapital());

            BigDecimal waitDeductAmount = BigDecimal.ZERO;

            if (StringUtils.isNotEmpty(deductOverdueFeeRequest.getDeductType())) {
                TradeDeductTypeEnum tradeDeductTypeEnum = TradeDeductTypeEnum.getEnum(deductOverdueFeeRequest.getDeductType());
                if (TradeDeductTypeEnum.TYPE1.equals(tradeDeductTypeEnum)) {
                    waitDeductAmount = DecimalUtils.round((overdueFee.doubleValue() + overdueServiceFee.doubleValue()) * 0.3, 2);
                } else if (TradeDeductTypeEnum.TYPE2.equals(tradeDeductTypeEnum)) {
                    waitDeductAmount = DecimalUtils.round((overdueFee.doubleValue() + overdueServiceFee.doubleValue()) * 0.5, 2);
                } else if (TradeDeductTypeEnum.TYPE3.equals(tradeDeductTypeEnum)) {
                    waitDeductAmount = DecimalUtils.round((overdueFee.doubleValue() + overdueServiceFee.doubleValue()) * 1, 2);
                } else if (TradeDeductTypeEnum.TYPE4.equals(tradeDeductTypeEnum)) {
                    waitDeductAmount = DecimalUtils.round((overdueFee.doubleValue() + overdueServiceFee.doubleValue()) * 1 + billBase.getInterestFee().doubleValue(), 2);
                } else if (TradeDeductTypeEnum.TYPE5.equals(tradeDeductTypeEnum)) {
                    waitDeductAmount = DecimalUtils.round(billBase.getInterestFee().doubleValue(), 2);
                } else {
                    waitDeductAmount = BigDecimal.ZERO;
                }
            } else {
                throw new ApplicationException("Deduction type cannot be empty");
            }

            billBase.setPaidCapital(realPaidCapital.add(waitDeductAmount));
            billBase.setDiscountCapital(waitDeductAmount);

            BigDecimal repaymentAmountAfter = CalculatorUtils.calculateRepaymentCapital(billBase.getRepaymentCapital(), overdueFee, overdueServiceFee, billBase.getPaidCapital());

            LOGGER.info("操作前后抵扣金额记录,billNo:{},old:{},new:{},repaymentAmount:{},overdueFee:{},overdueServiceFee:{},repaymentAmountAfter:{}",
                    billBase.getBillNo(), billBase.getDiscountCapital(), waitDeductAmount, repaymentAmount, overdueFee, overdueServiceFee, repaymentAmountAfter);
            //如果抵扣后剩余应还金额小于等于0 则不允许操作
            if (repaymentAmountAfter.setScale(2, BigDecimal.ROUND_HALF_UP).compareTo(BigDecimal.ZERO) <= 0) {
                LOGGER.error("抵扣后剩余金额小于等于0,不允许操作,tradeNo:{},repaymentAmountAfter:{}", billBase.getTradeNo(), repaymentAmountAfter);
                return Response.error("The remaining amount after deduction is less than 0, please operate through the financial system");
            }


            billBaseRepository.update(billBase);

            DeductFlow deductFlow = new DeductFlow();
            deductFlow.setTradeNo(billBase.getTradeNo());
            deductFlow.setBillNo(billBase.getBillNo());
            deductFlow.setDiscountCapital(waitDeductAmount);
            deductFlow.setOperator(deductOverdueFeeRequest.getOperator());
            deductFlow.setAccountedTime(date);
            deductFlow.setExpirationDate(expirationDate);
            deductFlowRepository.store(deductFlow);

            //删除之前的抵扣失效任务
            int num = tTimeoutCenterDao.deleteHisDeductTask(billBase.getBillNo());
            LOGGER.info("删除历史抵扣任务,billNo:{},num:{}", billBase.getBillNo(), num);
            if (expirationDate != null) {
                JSONObject j1 = new JSONObject();
                j1.put("billNo", billBase.getBillNo());
                j1.put("deductFlowId", deductFlow.getId());
                TimeoutEvent timeoutEvent = new TimeoutEvent()
                        .setType(TimeoutTypeEnum.DEDUCT_EXPIRATION)
                        .setTriggerTime(expirationDate)
                        .setValue(j1.toJSONString());
                timeoutRepository.store(timeoutEvent);
            }
            return Response.ok();
        } catch (Exception e) {
            LOGGER.error("deductOverdueFeeRequest处理失败", e);
            return Response.error("deduct error");
        }
    }

    /**
     * 获取订单状态
     *
     * @param orderNo
     * @return
     */
    @Override
    @TimerRecord
    public Response<OrderStatusResponse> orderStatus(String orderNo) {
        try {
            TradeBase tTradeEntity = tradeBaseRepository.load(orderNo);
            if (tTradeEntity == null) {
                return Response.error();
            }

            ProductCategory productCategory = productIntegrationService.getCategoryByMerchantCode(tTradeEntity.getMerchantCode());

            Map<String, String> map = new HashMap<>();
            map.put("order_no", orderNo);

            ReceiveData sendData = StandardUtil.assemblyReceiverData(JSON.toJSONString(map), SELF_PUBLIC_KEY, CO_PRIVATE_KEY, "gupta.order.status", productCategory.getMerchantCode());

            String response = commonRequest(JSON.toJSONString(sendData), productCategory.getMerchantPublicKey(), productCategory.getMerchantDoaminUrl());
            LOGGER.info("获取订单状态 请求三方结果:{}", response);
            JSONObject jsonObject = JSONObject.parseObject(response);

            if (!jsonObject.getString("code").equals("0")) {
                return Response.error("get open api data fail");
            }

            ProductCategory category = productIntegrationService.getCategoryByMerchantCode(tTradeEntity.getMerchantCode());
            if (category == null) {
                return Response.error("product config fail");
            }
            OrderStatusResponse statusDTO = JSONObject.parseObject(jsonObject.getString("data"), OrderStatusResponse.class);
            LOGGER.info("获取订单orderNo：{},状态为：{}", orderNo, statusDTO.getOrder_status());
            if (statusDTO.getOrder_status() != tTradeEntity.getTradeOrderStatus().getStatus()) {
                if (statusDTO.getOrder_status() == TradeStatusEnum.LOAN_WAITTING.getStatus()) {
                    if (tTradeEntity.getTradeOrderStatus() != TradeStatusEnum.PENDING_WITHDRAWAL) {
                        if (category.getConfirmWithdrawProcess() != null && Objects.requireNonNull(category).getConfirmWithdrawProcess().equals(1)) {
                            tradeBaseService.auditPassToWithdraw(tTradeEntity);
                        } else {
                            tradeBaseService.auditPass(tTradeEntity);
                        }
                    }
                } else if (statusDTO.getOrder_status() == TradeStatusEnum.PAY_RETRY.getStatus()) {
                    tradeBaseService.loanFailed(tTradeEntity, false);
                } else if (statusDTO.getOrder_status() == TradeStatusEnum.REPAYMENT_WATTING.getStatus()) {
                    if (tTradeEntity.getTradeOrderStatus() == TradeStatusEnum.PENDING_WITHDRAWAL) {
                        tradeBaseService.loanConfirmSuccess(tTradeEntity);
                    } else {
                        tradeBaseService.loanSuccess(tTradeEntity);
                    }
                } else if (statusDTO.getOrder_status() == TradeStatusEnum.AUDIT_REJECT.getStatus()) {
                    if (tTradeEntity.getTradeOrderStatus() != TradeStatusEnum.AUDIT_REJECT) {
                        tradeBaseService.closeAuditRejectTrade(tTradeEntity);
                        tradeUniqueService.releaseUniqueLock(tTradeEntity);
                    }
                } else if (statusDTO.getOrder_status() == TradeStatusEnum.EARLY_REPAYMENT.getStatus()) {
                    tradeBaseService.earlyPayment(tTradeEntity);
                    tradeUniqueService.releaseUniqueLock(tTradeEntity);
                } else if (statusDTO.getOrder_status() == TradeStatusEnum.NORMAL_REPAYMENT.getStatus()) {
                    tradeBaseService.normalPayment(tTradeEntity);
                    tradeUniqueService.releaseUniqueLock(tTradeEntity);
                } else if (statusDTO.getOrder_status() == TradeStatusEnum.OVERDUE_REPAYMENT.getStatus()) {
                    tradeBaseService.overduePayment(tTradeEntity);
                    tradeUniqueService.releaseUniqueLock(tTradeEntity);
                } else if (statusDTO.getOrder_status() == TradeStatusEnum.PENDING_WITHDRAWAL.getStatus()) {
                    tradeBaseService.auditPassToWithdraw(tTradeEntity);
                } else if (statusDTO.getOrder_status() == TradeStatusEnum.OVERDUE.getStatus()) {
                    tradeBaseService.overdue(tTradeEntity);
                }
                tradeBaseRepository.restore(tTradeEntity);
            }
            return Response.ok(statusDTO);
        } catch (Exception e) {
            LOGGER.error("获取订单状态失败", e);
            return Response.error("获取订单状态失败");
        }
    }

    /**
     * 获取还款计划
     *
     * @param orderNo
     * @return
     */
    @Override
    @TimerRecord
    public Response<RepaymentPlanDetailResponse> getPaymentPlan(String orderNo) {
        try {
            if (orderNo == null) {
                return Response.error("order no is null");
            }
            List<TBillEntity> billEntities = tBillDao.selectListByTradeNo(orderNo);
            TradeBase tTradeEntity = tradeBaseRepository.load(orderNo);
            if (tTradeEntity == null) {
                return Response.error("order no is exist");
            }
            ProductCategory productCategory = productIntegrationService.getCategoryByMerchantCode(tTradeEntity.getMerchantCode());


            Map<String, String> map = new HashMap<>();
            map.put("order_no", orderNo);

            ReceiveData sendData = StandardUtil.assemblyReceiverData(JSON.toJSONString(map), SELF_PUBLIC_KEY, CO_PRIVATE_KEY, "gupta.payment.plan", productCategory.getMerchantCode());

            String response = commonRequest(JSON.toJSONString(sendData), productCategory.getMerchantPublicKey(), productCategory.getMerchantDoaminUrl());
            LOGGER.info("获取还款计划 请求三方结果:{}", response);
            JSONObject jsonObject = JSONObject.parseObject(response);

            if (!jsonObject.getString("code").equals("0")) {
                return Response.error("get open api data fail");
            }

            RepaymentPlanDetailResponse planDetailVO = JSONObject.parseObject(jsonObject.getString("data"), RepaymentPlanDetailResponse.class);
            LOGGER.info("planDetailVO:{}", JSON.toJSONString(planDetailVO));
            buildRepaymentDetail(billEntities, tTradeEntity, planDetailVO);
            return Response.ok(planDetailVO);
        } catch (Exception e) {
            LOGGER.error("获取还款计划失败", e);
            return Response.error("获取还款计划失败");
        }
    }

    /**
     * 拉取审批结论
     *
     * @param orderNo
     * @return
     */
    @Override
    @TimerRecord
    public Response<ApproveConclusionResponse> approveConclusion(String orderNo) {
        try {
            if (orderNo == null) {
                return Response.error("order no is null");
            }
            TradeBase entity = tradeBaseRepository.load(orderNo);
            if (entity == null) {
                return Response.error();
            }
            ProductCategory productCategory = productIntegrationService.getCategoryByMerchantCode(entity.getMerchantCode());
            Map<String, String> map = new HashMap<>();
            map.put("order_no", orderNo);
            ReceiveData sendData = StandardUtil.assemblyReceiverData(JSON.toJSONString(map), SELF_PUBLIC_KEY, CO_PRIVATE_KEY, "gupta.audit.result", productCategory.getMerchantCode());
            String response = commonRequest(JSON.toJSONString(sendData), productCategory.getMerchantPublicKey(), productCategory.getMerchantDoaminUrl());
            LOGGER.info("拉取审批结论 请求三方结果:{}", response);
            JSONObject jsonObject = JSONObject.parseObject(response);

            if (!jsonObject.getString("code").equals("0")) {
                return Response.error("get open api data fail");
            }
            ApproveConclusionResponse approveConclusionResponse = JSONObject.parseObject(jsonObject.getString("data"), ApproveConclusionResponse.class);
            ProductCategory category = productIntegrationService.getCategoryByMerchantCode(entity.getMerchantCode());
            if (category == null) {
                return Response.error("product config fail");
            }
            if (approveConclusionResponse.getConclusion().equals(ApprovalConclusionEnum.PASS.getCode())) {
                if (entity.getTradeOrderStatus() != TradeStatusEnum.PENDING_WITHDRAWAL) {
                    if (category.getConfirmWithdrawProcess() != null && category.getConfirmWithdrawProcess().equals(1)) {
                        tradeBaseService.auditPassToWithdraw(entity);
                    } else {
                        tradeBaseService.auditPass(entity);
                    }
                }
                if (StringUtils.isNotEmpty(approveConclusionResponse.getApproval_time())) {
                    entity.setAuditDate(new Date(approveConclusionResponse.getApproval_time()));
                }
                tradeBaseRepository.restore(entity);
            } else if (approveConclusionResponse.getConclusion().equals(ApprovalConclusionEnum.REJECTED.getCode())) {
                if (entity.getTradeOrderStatus() != TradeStatusEnum.AUDIT_REJECT) {
                    tradeBaseService.closeAuditRejectTrade(entity);
                    tradeUniqueService.releaseUniqueLock(entity);
                }
                tradeBaseRepository.restore(entity);
                userIntegrationService.frozenUser(entity.getUserCode(), entity.getProductCategory(), FrozenCodeEnum.BORROW_REJECT);
            } else {
                return Response.error("approve conclusion code is wrong");
            }

            return Response.ok(approveConclusionResponse);
        } catch (Exception e) {
            LOGGER.error("拉取审批结论失败", e);
            return Response.error("拉取审批结论失败");
        }
    }

    /**
     * 创建open api 订单
     *
     * @param confirmationRequest
     * @return
     */
    @Override
    @Deprecated
    public Response createOpenApiOrder(SelfLoanConfirmationRequest confirmationRequest) {
        try {
            if (confirmationRequest == null) {
                return Response.error("order no is null");
            }
            TTradeEntity tTradeEntity = tTradeDao.selectOneByTradeNo(confirmationRequest.getOrder_no());
            ProductCategory productCategory = productIntegrationService.getCategoryByMerchantCode(tTradeEntity.getMerchantCode());
            ReceiveData data = new ReceiveData();
            data.setMethod("gupta.audit.result");
            data.setMerchant_id(productCategory.getMerchantCode());
            data.setBiz_data(JSON.toJSONString(confirmationRequest));
            String response = commonRequest(JSON.toJSONString(data), productCategory.getMerchantPublicKey(), productCategory.getMerchantDoaminUrl());

            JSONObject jsonObject = JSONObject.parseObject(response);
            if (!jsonObject.getString("code").equals("0")) {
                return Response.error("get open api data fail");
            }
            return Response.ok();
        } catch (Exception e) {
            LOGGER.error("创建open api 订单失败", e);
            return Response.error("创建open api 订单失败");
        }
    }

    private void pushMsg(BillPayRequest billPayRequest, String tradeNo, RepaymentMsgDto repaymentMsgDto) {
        String pushContent = tradeConfigUtil.msg_push_successful_repayment();
        String[] split = pushContent.split("\\|");
        String title = split[0];
        String content = split[1];
        content = content.replace("##AMOUNT##", repaymentMsgDto.getRepaymentCapital());
        String jumpPage = split[2];
        pushUtils.singleNotice(billPayRequest.getUserCode(), title, content, jumpPage);

        // 发送邮件
        String basicTemplateUrl = messageUtils.getEmailBasicTemplateUrl(billPayRequest.getUserCode(), tradeNo);
        String mailContent = HttpUtil.getPage(basicTemplateUrl);
        mailContent = mailContent.replace("__content__", content);
        mailContent = mailContent.replace("__Users Name__", Strings.nullToEmpty(repaymentMsgDto.getName()));
        mailContent = mailContent.replace("__Mobile No.__", Strings.nullToEmpty(repaymentMsgDto.getMobile()));
        mailContent = mailContent.replace("__Order ID__", Strings.nullToEmpty(tradeNo));

        messageIntegrationService.loanNoteByEmail(billPayRequest.getUserCode(), tradeNo, mailContent, title);
    }


    /**
     * 异步发送短信
     * 发送还款完成短信
     * 如果发送失败
     * 自己处理不影响还款逻辑
     *
     * @param billPayRequest
     * @param repaymentMsgDto
     */
    private void sendRepaymentSucessMsg(BillPayRequest billPayRequest, RepaymentMsgDto repaymentMsgDto) {
        try {
            User user = userIntegrationService.getUserByUserCode(billPayRequest.getUserCode());
            repaymentMsgDto.setMobile(user.getMobile());
            repaymentMsgDto.setName(user.getName());
            LOGGER.info("sendRepaymentSucessMsg repaymentMsgDto:{}", JSON.toJSON(repaymentMsgDto));
            messageIntegrationService.sendRepaymentNote(repaymentMsgDto, billPayRequest.getUserCode(), billPayRequest.getTradeNo());
        } catch (Exception e) {
            LOGGER.error("=发送还款完成短信失败=,e", e);
        }
    }

    /**
     * 还款
     *
     * @param billBase
     * @param billPayRequest
     */
    private BillPaidStatusEnum repayment(BillBase billBase, BillPayRequest billPayRequest, TradeBillDetail tradeBillDetail) {
        BigDecimal totalCapital = new BigDecimal(billPayRequest.getCapital());
        LOGGER.info("== 还款 ==,billNo:{},date:{},totalCapital:{}", billBase.getBillNo(), billPayRequest.getPayDate(), totalCapital);

        if (BillPaidStatusEnum.CLEAR_REPAYMENT.equals(billBase.getPaidStatus())) {
            LOGGER.info("== 还款失败 ==,已还清");
            return BillPaidStatusEnum.CLEAR_REPAYMENT;
        }

        TradeBase tradeBase = tradeBaseRepository.load(billBase.getTradeNo());
        if (tradeBase == null || !tradeBase.getUserCode().equals(billPayRequest.getUserCode())) {
            LOGGER.error("== 还款失败 ==,交易不存在");
            throw new TradeException(TradeErrorCodeEnum.trade_not_exist);
        }

        BigDecimal discountCapital = billPayRequest.getDiscountCapital();//抵扣金额

        LOGGER.info("== 还款 ==,billNo:{},date:{},totalCapital:{},discountCapital:{}", billBase.getBillNo(), billPayRequest.getPayDate(), totalCapital, discountCapital);
        //更新账单信息
        billBaseDomainFactory.composeBillBaseForRepayment(billBase, tradeBase, billPayRequest, discountCapital);
        billBaseRepository.update(billBase);

        userIntegrationService.updateUserAuthStatus(tradeBase.getUserCode(), UserAuthEnum.IS_OLD_USER, UserAuthStatusEnum.SUCCESS);

        //优惠核销
        if (discountCapital.compareTo(BigDecimal.ZERO) != 0) {
            tradeDiscountService.verifyDiscount(billBase, billPayRequest);
        }

        //做当次还款-当笔账单的-会计分录
        TradeRepaymentAccounting tradeRepaymentAccounting = billBaseDomainFactory.composeBillRepaymentAccounting(
                tradeBillDetail, billBase, billPayRequest, discountCapital);
        billRepaymentAccountingRepository.store(tradeRepaymentAccounting);
        try {
            List<BillAccounting> billAccountingList = billAccountingRepository.getBillAccountingByBillNo(billBase.getBillNo());
            //基于现有的accounting产生一条新的记账
            BillAccounting billAccounting = billBaseDomainFactory.composeBillAccounting(billAccountingList, billBase, tradeBase, billPayRequest, tradeBillDetail);
            billAccountingRepository.store(billAccounting);
        } catch (Exception e) {
            LOGGER.error("会计分录记账失败,billNo:{}", billBase.getBillNo(), e);
        }

        //这里作减法,目的是用来计算多笔账单的结清 目前都是单期账单,所以没什么用
        billPayRequest.setCapital(totalCapital.subtract(billBase.getPaidCapital()).toString());

        //逾期超过指定天数 进行用户冷冻
        int overDueDays = CalculatorUtils.overDueDays(billBase.getRepaymentDate(), billBase.getPayOffDate());
        if (overDueDays >= tradeConfigUtil.getOverdueFrozenThreshold()) {
            userIntegrationService.frozenUser(billPayRequest.getUserCode(), billBase.getProductCategory(), FrozenCodeEnum.REPAYMENT_OVERDUE);
        }

        return billBase.getPaidStatus();
    }


    private void addToTaskCenter(TradeBillDetail tradeBillDetail) {
        LOGGER.info("== 更新催收的金额失败,放入任务中心 ==,billNo:{},bizNo:{}", tradeBillDetail.getBillNo(), tradeBillDetail.getBizNo());
        TradeTaskCenter tradeTaskCenter = new TradeTaskCenter()
                .setTaskRetryTimes(0)
                .setTaskStatus(TaskStatusEnum.INIT)
                .setTaskType(TaskTypeEnum.COLLECTION_PAID_PUSH)
                .setTradeNo(tradeBillDetail.getTradeNo())
                .setBizNo(tradeBillDetail.getBizNo());

        tradeTaskCenterRepository.store(tradeTaskCenter);
    }


    private void updateTrade(RepaymentMsgDto repaymentMsgDto, List<String> tradeNoList,
                             Date billPayDate) {
        for (String tradeNo : tradeNoList) {
            TradeBase tradeBase = tradeBaseRepository.load(tradeNo);

            if (repaymentMsgDto != null) {
                repaymentMsgDto.setBorrowTotalCapital(String.valueOf(tradeBase.getBorrowTotalCapital()))
                        .setProductCode(tradeBase.getProductCode());
            }
            boolean completed = tradeBase.getTradeOrderStatus().equals(TradeStatusEnum.EARLY_REPAYMENT) ||
                    tradeBase.getTradeOrderStatus().equals(TradeStatusEnum.OVERDUE_REPAYMENT) ||
                    tradeBase.getTradeOrderStatus().equals(TradeStatusEnum.OVERDUE_REPAYMENT);
            if (completed) {
                LOGGER.info("== 交易已经还清,不需要再更新信息 ==,tradeNo:{}", tradeNo);
                continue;
            }
            List<BillBase> billBaseList = billBaseRepository.getBillListByTradeNo(tradeNo);
            if (CollectionUtils.isEmpty(billBaseList)) {
                LOGGER.error("==== trade repayment,trade do not contain bill,tradeNo:{}", tradeNo);
                continue;
            }

            BigDecimal totalOverdueServiceFee = BigDecimal.valueOf(0);
            BigDecimal totalOverdueFee = BigDecimal.valueOf(0);
            BigDecimal paidTotalCapital = BigDecimal.valueOf(0);
            //BigDecimal discountTotalCapital = BigDecimal.ZERO;
            int paidPeriod = 0;

            // 是否有逾期未还
            boolean hasOverDue = false;
            for (BillBase billBase : billBaseList) {
                totalOverdueServiceFee = totalOverdueServiceFee.add(billBase.getOverdueServiceFee());
                totalOverdueFee = totalOverdueFee.add(billBase.getOverdueFee());

                paidTotalCapital = paidTotalCapital.add(billBase.getPaidCapital());
                //discountTotalCapital = discountTotalCapital.add(billBase.getDiscountCapital());

                if (billBase.getPaidStatus().equals(BillPaidStatusEnum.CLEAR_REPAYMENT)) {
                    paidPeriod++;
                } else {//判断未结清的账单的订单中是否有逾期
                    hasOverDue = hasOverDue || billBase.isOverdue();
                }
            }

            tradeBase.setTotalOverdueFee(totalOverdueFee)
                    .setTotalOverdueServiceFee(totalOverdueServiceFee)
                    .setPaidTotalCapital(paidTotalCapital)
                    //.setDiscountTotalCapital(discountTotalCapital)
                    .setPaidPeriod(paidPeriod);

            if (tradeBase.getTotalPeriod() == paidPeriod) {
                tradeBase.setPayOffDate(billPayDate);
                if (tradeBase.getTradeOrderStatus().equals(TradeStatusEnum.OVERDUE)) {
                    tradeBase.transferStatus(TradeActionEnum.OVERDUE_RECEIPT.getCode(),
                            TradeStatusEnum.OVERDUE_REPAYMENT.getStatus(), "jeff", new Date(), new Date(), "repayment clear");
                } else {
                    if (DateUtil.diffDate(billPayDate, billBaseList.get(0).getRepaymentDate()) == 0) {
                        tradeBase.transferStatus(TradeActionEnum.PAID_RECEIPT.getCode(),
                                TradeStatusEnum.NORMAL_REPAYMENT.getStatus(), "jeff", new Date(), new Date(), "repayment clear");
                    } else {
                        tradeBase.transferStatus(TradeActionEnum.PAID_RECEIPT.getCode(),
                                TradeStatusEnum.EARLY_REPAYMENT.getStatus(), "jeff", new Date(), new Date(), "repayment clear");
                    }
                }
                tradeUniqueService.releaseUniqueLock(tradeBase);

                //结清之后推送信息给usha -（需要是订单到账时间在APi启用之后）
                if (tradeBase.getChannelCode().contains("open_api")) {
                    JSONObject ext = new JSONObject();
                    ext.put("billNo", billBaseList.get(0).getBillNo());
                    MQOpenapiHelpUtils.send(tradeNoList.get(0), MQTradeActionEnum.REPAY_SUCCESS, ext);
                }
                //订单结清内部埋点
                userIntegrationService.addUserEventTrack(tradeBase.getUserCode(), UserEventTrackEnum.e_app_order_payment_done);
                //未结清的账单中不存在逾期订单  将trade订单从逾期更新为 待还款
            } else if (!hasOverDue && tradeBase.getTradeOrderStatus().equals(TradeStatusEnum.OVERDUE)) {
                tradeBase.transferStatus(TradeActionEnum.OVERDUE_TO_REPAYMENT_WAITTING.getCode(),
                        TradeStatusEnum.REPAYMENT_WATTING.getStatus(), "jeff", new Date(), new Date(), "repayment clear");
            }

            tradeBaseRepository.restore(tradeBase);
        }
    }


    public String commonRequest(String body, String token, String url) {
        try {
            Map<String, String> headers = new HashMap<>();
            headers.put("token", token);
            headers.put("Content-Type","application/json");
            return HttpUtil.postPageBody(url, headers, body);
        } catch (Exception e) {
            LOGGER.error("请求open-api出错。。。", e);
            return null;
        }

    }

    /**
     * 还款计划
     *
     * @param billEntities
     * @param tTradeEntity
     * @param planDetailVO
     */
    private void buildRepaymentDetail(List<TBillEntity> billEntities, TradeBase tTradeEntity, RepaymentPlanDetailResponse planDetailVO) {
        LOGGER.info("============   获取还款计划  buildRepaymentDetail ==========");

        tTradeEntity.setBorrowTotalCapital(new BigDecimal(planDetailVO.getLoan_amount()));
        tTradeEntity.setReceivedTotalCapital(new BigDecimal(planDetailVO.getReceived_capital()));
        tTradeEntity.setRepaymentTotalCapital(new BigDecimal(planDetailVO.getRepay_amount()));
        tTradeEntity.setBorrowRatio(new BigDecimal(planDetailVO.getInterest_rate()));
        if (planDetailVO.getLoan_time() != 0) {
            tTradeEntity.setReceivedDate(new Date(planDetailVO.getLoan_time()));
        }
        TBillEntity entity;
        if (CollectionUtils.isEmpty(billEntities)) {
            entity = new TBillEntity();
            entity.setBillNo(IdUtils.genId() + "TB");
        } else {
            entity = billEntities.get(0);
        }
        if (planDetailVO.getBill_status() == BillPaidStatusEnum.CLEAR_REPAYMENT.getStatus()) {
            entity.setStatus(1);
            entity.setPaidStatus(BillPaidStatusEnum.CLEAR_REPAYMENT.getStatus());
            entity.setPayOffDate(new Date());
            entity.setOverdueFee(new BigDecimal(0));
            entity.setOverdueServiceFee(new BigDecimal(0));
            if (!planDetailVO.getOrder_status().equals(String.valueOf(tTradeEntity.getTradeOrderStatus().getStatus()))) {
                if (planDetailVO.getOrder_status().equals(OrderStatusSwitchEnum.EARLIER_REPAY_SUCCESS.getCode().toString())) {
                    tradeBaseService.earlyPayment(tTradeEntity);
                } else if (planDetailVO.getOrder_status().equals(OrderStatusSwitchEnum.OVERDUE_REPAY_SUCCESS.getCode().toString())) {
                    tradeBaseService.overduePayment(tTradeEntity);
                } else if (planDetailVO.getOrder_status().equals(OrderStatusSwitchEnum.REPAY_SUCCESS.getCode().toString())) {
                    tradeBaseService.normalPayment(tTradeEntity);
                }
            }
        } else {
            entity.setStatus(BillPaidStatusEnum.INIT.getStatus());
            entity.setPaidStatus(BillPaidStatusEnum.INIT.getStatus());
        }
        entity.setTradeNo(tTradeEntity.getTradeNo());
        entity.setBorrowCapital(new BigDecimal(planDetailVO.getLoan_amount()));
        entity.setReceivedCapital(new BigDecimal(planDetailVO.getReceived_capital()));
        entity.setInterestFee(new BigDecimal(planDetailVO.getInterest_amount()));
        entity.setBorrowServiceFee(new BigDecimal(planDetailVO.getAdmin_amount()));
        entity.setRepaymentCapital(new BigDecimal(planDetailVO.getRepay_amount()));
        entity.setRepaymentDate(new Date(planDetailVO.getExpiry_time()));
        entity.setCurrentPeriod(1);
        entity.setProductCategory(tTradeEntity.getProductCategory());
        entity.setProductCode(tTradeEntity.getProductCode());
        entity.setPaidCapital(new BigDecimal("0"));
        entity.setDiscountCapital(new BigDecimal("0"));
        entity.setCreateDt(new Date());
        if (StringUtils.isNotEmpty(planDetailVO.getPaidCapital()))
            entity.setPaidCapital(new BigDecimal(planDetailVO.getPaidCapital()));
        if (StringUtils.isNotEmpty(planDetailVO.getOverdue_service_fee()))
            entity.setOverdueServiceFee(new BigDecimal(planDetailVO.getOverdue_service_fee()));
        if (planDetailVO.getBill_status() == 3 && StringUtils.isNotEmpty(planDetailVO.getOverdue_service_fee())) {
            entity.setOverdueServiceFee(new BigDecimal(planDetailVO.getOverdue_service_fee()));
        }
        if (planDetailVO.getBill_status() == 3) {
            entity.setOverdueFee(new BigDecimal(planDetailVO.getExpiry_amount()));
        }
        //更新trade
        tradeBaseRepository.restore(tTradeEntity);
        //更新bill
        if (entity.getId() == null || entity.getId() == 0L) {
            tBillDao.insert(entity);
        } else {
            tBillDao.updateByPrimaryKey(entity);
        }
    }

    public static void main(String[] args) {

        System.out.println(new Date(1607365800000l).toString());
    }
}
